/**
 * @file disk_fatfs_nor.c
 * @author LokLiang (lokliang@163.com)
 * @brief 添加 storage_nor.h 数据结构到实现 FATFS 专用的移植接口数据结构的代码
 * 依赖关系：
 * fatfs --> SFUD --> sim_spi_flash
 * @version 0.1
 * @date 2023-06-16
 *
 * @copyright Copyright (c) 2023
 *
 */

#if defined(MIX_FATFS)

#include "drivers/ext/disk_fatfs.h"
#include "components/storage_nor.h"
#include "fs_init_fatfs.h"

#include <unistd.h>
#include <fcntl.h>

#define IMG_DISK_SECTION_SIZE (512)
#define IMG_DISK_SECTION_COUNT (1024)

static DSTATUS _fatfs_disk_status(void);
static DSTATUS _fatfs_disk_initialize(void);
static DRESULT _fatfs_disk_read(BYTE *buff, LBA_t sector, UINT count);
static DRESULT _fatfs_disk_write(const BYTE *buff, LBA_t sector, UINT count);
static DRESULT _fatfs_disk_ioctl(BYTE cmd, void *buff);

static device_disk_fatfs_data_t s_data;

static struct device_disk_fatfs const s_device = {
    .api = {
        .disk_status = _fatfs_disk_status,
        .disk_initialize = _fatfs_disk_initialize,
        .disk_read = _fatfs_disk_read,
        .disk_write = _fatfs_disk_write,
        .disk_ioctl = _fatfs_disk_ioctl,
    },
    .data = &s_data,
};
OBJ_EXPORT_DEVICE(s_device, "disk:/ff:nor");

static struct
{
    storage_nor_t nor_hdl;
    uint16_t sector_size;
    uint16_t sector_count;
} s_cm;

static DSTATUS _fatfs_disk_status(void)
{
    if (s_cm.nor_hdl)
        return RES_OK;
    else
        return RES_ERROR;
}

static DSTATUS _fatfs_disk_initialize(void)
{
    const char *name;

    name = "SPI:SIM";
    const char *dev_name = device_name(device_binding(name));
    if (dev_name == NULL)
    {
        return RES_ERROR;
    }

    name = "NOR:";
    const char *nor_name = nor_search(name, 0);
    if (nor_name == NULL)
    {
        return RES_ERROR;
    }

    s_cm.nor_hdl = nor_binding(nor_name);
    if (s_cm.nor_hdl == NULL)
    {
        return RES_ERROR;
    }

    if (nor_init(s_cm.nor_hdl, dev_name, NULL) == 0)
    {
        s_cm.sector_size = nor_get_sector_size(s_cm.nor_hdl);
        if (s_cm.sector_size)
        {
            s_cm.sector_count = nor_get_chip_size(s_cm.nor_hdl) / s_cm.sector_size;
            if (s_cm.sector_count)
            {
                return RES_OK;
            }
        }
    }

    s_cm.nor_hdl = NULL;
    return RES_ERROR;
}

static DRESULT _fatfs_disk_read(BYTE *buff, LBA_t sector, UINT count)
{
    if (s_cm.nor_hdl)
    {
        if (nor_read(s_cm.nor_hdl, buff, s_cm.sector_size * sector, s_cm.sector_size * count) == 0)
        {
            return RES_OK;
        }
    }
    return RES_ERROR;
}

static DRESULT _fatfs_disk_write(const BYTE *buff, LBA_t sector, UINT count)
{
    if (s_cm.nor_hdl)
    {
        if (nor_erase(s_cm.nor_hdl, s_cm.sector_size * sector, s_cm.sector_size * count) == 0)
        {
            if (nor_write(s_cm.nor_hdl, s_cm.sector_size * sector, buff, s_cm.sector_size * count) == 0)
            {
                return RES_OK;
            }
        }
    }
    return RES_ERROR;
}

static DRESULT _fatfs_disk_ioctl(BYTE cmd, void *buff)
{
    switch (cmd)
    {
    case CTRL_SYNC:
        break;
    case GET_SECTOR_COUNT:
        *(WORD *)buff = s_cm.sector_count;
        break;
    case GET_SECTOR_SIZE:
        *(WORD *)buff = s_cm.sector_size;
        break;
    case GET_BLOCK_SIZE:
        *(WORD *)buff = 1;
        break;
    case CTRL_TRIM:
        break;
    }
    return RES_OK;
}

static int _init_fatfs(void)
{
    device_disk_fatfs_t dev = &s_device;
    if (f_disk_regist(dev, "nor", -1) == 0)
    {
        /* 挂载 */
        FRESULT res = f_mount(&dev->data->fs, dev->data->volume_str, 1);
        if (res == FR_NO_FILESYSTEM)
        {
            BYTE work[FF_MAX_SS];
            res = f_mkfs(dev->data->volume_str, 0, work, sizeof(work));
            res = f_mount(NULL, dev->data->volume_str, 0);           // 取消文件系统
            res = f_mount(&dev->data->fs, dev->data->volume_str, 1); // 挂载文件系统
        }
        if (res != FR_OK)
        {
            f_disk_unregist("nor");
            return -1;
        }
        return 0;
    }
    return -1;
}
INIT_EXPORT_DEVICE(_init_fatfs);

#endif /* #if defined(MIX_FATFS) */
